package models

import (
	"cloudops/src/common"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"strconv"
)

// 字段说明 https://help.aliyun.com/zh/ecs/developer-reference/api-describeinstances?spm=a2c4g.11186623.0.i1#t9865.html
type ResourceEcs struct {
	Model // 不用每次写ID 和 createAt了
	ResourceCommon
	// 核心字段
	// uid 代表不会变的 id ，除非被删除

	// 常见字段
	OsType       string `json:"OsType" gorm:"comment:操作系统类型 win linux" `
	VmType       int    `json:"VmType" gorm:"default:1;comment:角色是否被冻结 =1云厂商虚拟设备 =2物理设备 agent上报的"`
	InstanceType string `json:"InstanceType" gorm:"comment:实例类型 ecs.g8a.2xlarge  https://www.alibabacloud.com/help/zh/ecs/user-guide/overview-of-instance-families " `

	// 资源
	Cpu               int         `json:"Cpu" gorm:"comment:vCPU数"`
	Memory            int         `json:"Memory" gorm:"comment:内存大小，单位为GiB。"`
	Disk              int         `json:"Disk" gorm:"comment:磁盘打下，单位为GiB。"`
	OSName            string      `json:"OSName" gorm:"comment:实例的操作系统名称。 CentOS 7.4 64 位"`
	ImageId           string      `json:"ImageId" gorm:"comment:镜像模板" `
	Hostname          string      `json:"Hostname" gorm:"type:varchar(100);comment:主机名" `
	NetworkInterfaces StringArray `json:"NetworkInterfaces" gorm:"comment: []弹性网卡id集合"`
	DiskIds           StringArray `json:"DiskIds" gorm:"comment: [d-bp67acfmxazb4p]云盘ID"`

	// 有关时间的
	StartTime       string `json:"StartTime" gorm:"comment:实例最近一次的启动时间。以ISO 8601为标准，并使用UTC+0时间，格式为yyyy-MM-ddTHH:mmZ。更多信息，请参见ISO 8601。"  `
	AutoReleaseTime string `json:"AutoReleaseTime" `
	LastInvokedTime string `json:"LastInvokedTime" `

	BindNodes []*StreeNode `json:"bind_nodes" gorm:"many2many:bind_ecss;"` // 多对多

}

type EcsBuyWorkOrder struct {
	Vendor         string `json:"vendor"`
	Num            int    `json:"num"`
	BindLeafNodeId int    `json:"bindLeafNodeId"`
	InstanceType   string `json:"instance_type"`
	Hostnames      string `json:"hostnames"` // split \n 多条记录
}

// 创建ESC资源
func (obj *ResourceEcs) Create() error {
	return DB.Create(obj).Error

}

// 删除ECS资源
func (obj *ResourceEcs) DeleteOne() error {
	return DB.Select(clause.Associations).Unscoped().Delete(obj).Error
}

// 级联删除
func DeleteResourceEcsOneByInstanceId(iid string) error {
	return DB.Select(clause.Associations).Unscoped().Where("instance_id = ?", iid).Delete(&ResourceEcs{}).Error
}

// 添加ECS
func (obj *ResourceEcs) CreateOne() error {
	return DB.Create(obj).Error

}

// 更新
func (obj *ResourceEcs) UpdateOne() error {
	return DB.Where("id = ?", obj.ID).Updates(obj).Error
}

// 更新绑定节点
func (obj *ResourceEcs) UpdateBindNodes(nodes []*StreeNode) error {
	return DB.Debug().Model(obj).Association("BindNodes").Replace(nodes)
}

// 获取ESC的uid和hash值
func GetResourceEcsUidAndHash() (map[string]string, error) {
	var objs []*ResourceEcs
	// 排除=2的 agent上报的
	err := DB.Where("vm_type=1").Find(&objs).Error
	if err != nil {
		return nil, err
	}
	m := make(map[string]string)
	for _, h := range objs {
		m[h.InstanceId] = h.Hash
	}
	return m, nil
}

// 获取ESC的instanceId
func GetResourceEcsByInstanceId(instanceId string) (*ResourceEcs, error) {
	var dbObj ResourceEcs
	err := DB.Where("instance_id = ?", instanceId).Preload("BindNodes").First(&dbObj).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf(common.ERR_ECS_NOT_FOUND)
		}
		return nil, fmt.Errorf("数据库错误:%w", err)
	}
	return &dbObj, nil
}

// 生成hash值
func (rh *ResourceEcs) GenHash() string {
	h := md5.New()
	h.Write([]byte(rh.InstanceId))
	h.Write([]byte(rh.InstanceType))
	h.Write([]byte(strconv.Itoa(rh.Cpu)))
	h.Write([]byte(strconv.Itoa(rh.Memory)))
	h.Write([]byte(strconv.Itoa(rh.Disk)))
	return hex.EncodeToString(h.Sum(nil))
}

func (obj *ResourceEcs) FillFrontAllData() {
	obj.Key = fmt.Sprintf("%d", obj.ID)
}

// GetResourceEcsAll 获取所有ECS
func GetResourceEcsAll() (objs []*ResourceEcs, err error) {
	err = DB.Debug().Preload("BindNodes").Find(&objs).Error
	return
}

// 根据ECS的ID获取绑定对象
func GetResourceEcsById(id int) (*ResourceEcs, error) {
	var dbObj ResourceEcs
	err := DB.Where("id = ?", id).Preload("BindNodes").First(&dbObj).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("ResourceEcs不存在")
		}
		return nil, fmt.Errorf("数据库错误:%w", err)
	}
	return &dbObj, nil

}

// GetResourceEcsByIdsWithLimitOffset 做分页处理
func GetResourceEcsByIdsWithLimitOffset(ids []int, limit, offset int) (objs []*ResourceEcs, err error) {
	err = DB.Limit(limit).Offset(offset).Find(&objs, ids).Error
	if objs != nil {
		for _, obj := range objs {
			obj := obj
			obj.FillFrontAllData()
		}
	}
	return
}

// 获取特定服务树节点绑定的资源列表， 给删除叶子节点时做校验如果绑定了资源，则不允许删除
func GetResourceEcsBoundToNode(nodeID uint) ([]*ResourceEcs, error) {
	allEcs, err := GetResourceEcsAll()
	if err != nil {
		return nil, err
	}

	var boundResources []*ResourceEcs
	for _, ecs := range allEcs {
		ecs := ecs
		for _, bindNode := range ecs.BindNodes {
			if bindNode.ID == nodeID {
				boundResources = append(boundResources, ecs)
				break
			}
		}
	}

	return boundResources, nil
}

// 封装先查询后更新的方法
func (obj *ResourceEcs) CreateOrUpdate() error {
	if DB.Debug().Where("instance_id = ?", obj.InstanceId).Updates(obj).RowsAffected == 0 {
		return obj.CreateOne()
	}
	return nil
}

// 对比一下不加preload的速度
func GetResourceEcsAllNoPreload() (objs []*ResourceEcs, err error) {
	err = DB.Debug().Find(&objs).Error
	return
}
